package com.roadjava.controller;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLDataType;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlCreateTableParser;
import com.alibaba.druid.util.lang.Consumer;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.roadjava.bean.ClassDefinition;
import com.roadjava.bean.FieldDefinition;
import com.roadjava.res.ResultDTO;
import com.roadjava.util.FreemarkerUtil;
import com.roadjava.util.ResponseUtil;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

/**
 * @author zhaodaowen
 * @see <a href="http://www.roadjava.com">乐之者java</a>
 */
@WebServlet(name = "assistController",value = "/assist")
public class AssistController extends HttpServlet{
    private static final Map<String,String> typeMapping = new HashMap<>();
    static {
        typeMapping.put("char","String");
        typeMapping.put("varchar","String");
        typeMapping.put("tinytext","String");
        typeMapping.put("meduimtext","String");
        typeMapping.put("longtext","String");
        typeMapping.put("text","String");

        typeMapping.put("tinyint","Byte");
        typeMapping.put("samllint","Integer");
        typeMapping.put("int","Integer");
        typeMapping.put("bigint","Long");

        typeMapping.put("float","Float");

        typeMapping.put("double","Double");
        typeMapping.put("numberic","Double");
        typeMapping.put("decimal","Double");

        typeMapping.put("timestamp","Date");
        typeMapping.put("datetime","Date");
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // servlet接受请求类型为application/json的请求，需要先转化一下，
        // 不能接收到application/x-www-form-urlencoded类型的
        JSONObject reqParam = new JSONObject();
        try(BufferedReader br = new BufferedReader(new InputStreamReader(req.getInputStream(),"utf-8"))) {
            String line;
            StringBuilder jsonStr = new StringBuilder();
            while ((line = br.readLine()) != null) {
                jsonStr.append(line);
            }
            if (jsonStr.length() > 0) {
                reqParam = JSON.parseObject(jsonStr.toString());
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        String flag = reqParam.getString("flag");
        String cont = reqParam.getString("cont");

        // 这种方式只能接受到Content-Type: application/x-www-form-urlencoded的请求类型
//        String flag = req.getParameter("flag");
//        String cont = req.getParameter("cont");
        if ("generateDO".equals(flag)) {
            ClassDefinition classDefinition;
            try {
                classDefinition = parseCreateTableSql(cont);
                String result = FreemarkerUtil.parse2Str("generate_do.ftl", classDefinition);
                ResponseUtil.respAppJson(resp,ResultDTO.buildSuccess(result));
            } catch (Exception e) {
                e.printStackTrace();
                ResponseUtil.respAppJson(resp,ResultDTO.buildFailure(e.getMessage()));
            }
        }
    }

    private ClassDefinition parseCreateTableSql(String cont) {
        ClassDefinition ret = new ClassDefinition();
        MySqlCreateTableParser parser = new MySqlCreateTableParser(cont);
        MySqlCreateTableStatement createObj = (MySqlCreateTableStatement) parser.parseCreateTable();
        String tableName = canonicalizeEle(createObj.getName().getSimpleName());
        ret.setClassName(tableName);
        List<FieldDefinition> fields = new ArrayList<>();
        createObj.forEachColumn(sqlColumnDefinition -> {
            FieldDefinition fd = new FieldDefinition();
            String name = canonicalizeEle(sqlColumnDefinition.getNameAsString());
            String dataType = canonicalizeDataType(sqlColumnDefinition.getDataType().getName());
            String comment = sqlColumnDefinition.getComment() != null ?
                    sqlColumnDefinition.getComment().toString() : null;
            if (StringUtils.isNotBlank(comment)) {
                // '学号',去掉单引号
                comment = comment.replace("'","");
            }
            fd.setName(name);
            String javaType = typeMapping.get(dataType);
            if (StringUtils.isBlank(javaType)) {
                throw  new RuntimeException(dataType + "找不到对应的java类型");
            }
            fd.setType(javaType);
            fd.setComment(comment);
            fields.add(fd);
        });
        ret.setFields(fields);
        return ret;
    }

    private String canonicalizeDataType(String dataType) {
        // varchar(50),去掉(50)
        if (StringUtils.isBlank(dataType)) {
            throw new RuntimeException("建表语句格式不正确,请检查字段类型");
        }
        dataType = dataType.trim().toLowerCase();
        if (dataType.contains("(")) {
            dataType = dataType.substring(0,dataType.indexOf("("));
        }
        return dataType;
    }

    private String canonicalizeEle(String ele) {
        if (StringUtils.isBlank(ele)) {
            throw new RuntimeException("没有表名或字段");
        }
        // 有的字段或表名上有"`"符号
        String tmp = ele.replace("`","").toLowerCase().trim();
        // 下划线转驼峰
        String[] split = tmp.split("_");
        StringBuilder sb = new StringBuilder();
        Stream.of(split).forEach(s -> sb.append(StringUtils.capitalize(s)));
        return sb.toString();
    }
}
